#ifndef __YID_H__
#define __YID_H__

#include <stdint.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <stdio.h>
#include <assert.h>
#include <limits.h>

#include "layout.h"
#include "sysy_conf.h"
#include "lich_id.h"
#include "configure.h"

extern int srv_running;
extern int rdma_running;

/**
 * ID region
 */
#define FILEID_NULL     0
#define FILEVER_NULL    0

#define OID_NULL      0
#define OVER_NULL     0

#define CHKID_NULL      0
#define CHKVER_NULL     0

#define ROOTID 1

#pragma pack(8)

typedef struct {
        uintptr_t local_addr;
        uintptr_t remote_addr;
        uint32_t  lkey;
        uint32_t  rkey;
        uint32_t  size;
} data_prop_t;

typedef struct {
        uint32_t idx;
        uint32_t figerprint;
        uint16_t tabid;
        uint32_t opcode;
        data_prop_t data_prop;
} msgid_t;

#pragma pack()

#pragma pack(8)

typedef struct {
        uint64_t id;
        uint32_t version;
        uint32_t status;        /* Unavailable */
} verid64_t;

#pragma pack()

typedef enum {
        __NULL_CHUNK__, /*0*/
        __POOL_CHUNK__, /*1*/
        __POOL_SUB_CHUNK__, /*2*/
        __VOLUME_CHUNK__, /*3*/
        __VOLUME_SUB_CHUNK__, /*4*/
        __RAW_CHUNK__, /*5*/
} chunk_type2str_t;

typedef enum {
        CHUNK_STORAGE_UN = 0,
        CHUNK_STORAGE_SSD = 1,
        CHUNK_STORAGE_HD = 2,
        CHUNK_STORAGE_COUNT = 3,
} chunk_storage_t;

#pragma pack(1)

typedef struct {
        uint64_t vfm;
        uint64_t clock;
} vclock_t;

typedef struct {
        chkid_t id;
        vclock_t vclock;
        uint64_t lease;
        union {
                uint64_t offset;
                struct {
                        uint32_t chunk_off:20;  // 1M
                        uint32_t chunk_id:32;
                        uint32_t __pad:12;
                };
        };
        uint32_t size;
        uint32_t flags;
        uint64_t lsn;
} io_t;
#pragma pack()

#define VCLOCK_FORMAT   "vclock:%ju.%ju"
#define VCLOCK_ARG(__vclock__)  (__vclock__)->clock, (__vclock__)->vfm

typedef io_t lsv_io_t;

static inline void io_init(io_t *io, const chkid_t *chkid,  const vclock_t *vclock,
                           uint64_t offset, uint32_t size, uint32_t flags)
{
        memset(io, 0 ,sizeof(io_t));
        
        if(chkid)
                io->id = *chkid;

        if (vclock) {
                //assert((uint64_t)vclock > (uint64_t)100);
                io->vclock = *vclock;
        } else {
                io->vclock.vfm = 0;
                io->vclock.clock = 0;
        }
        
        io->offset = offset;
        io->size = size;
        io->flags = flags;
        io->lsn = 0;
        io->lease = -1;
}

static inline void lsv_io_init(lsv_io_t *io, const chkid_t *chkid, uint32_t chunk_id, uint32_t chunk_off,
                               uint32_t size, uint32_t flags)
{
        io->id = *chkid;
        io->vclock.vfm = 0;
        io->vclock.clock = 0;
        io->chunk_off = chunk_off;
        io->chunk_id = chunk_id;
        io->__pad = 0;
        io->size = size;
        io->flags = flags;
}

typedef struct {
        uint64_t lsn;
        uint64_t off;
        uint32_t size;
} simple_io_t;

static inline void simple_io_init(simple_io_t *sio, uint64_t lsn, uint64_t off, uint32_t size) {
        sio->lsn = lsn;
        sio->off = off;
        sio->size = size;
}

typedef struct{
	uint64_t off;
	uint32_t size;
}raw_io_t;

static inline void raw_io_init(raw_io_t *rio, uint64_t off, uint32_t size) {
        rio->off = off;
        rio->size = size;
}

#define CHUNK_TYPE "null", "pool", "subpool", "vol", "subvol", "raw"

static inline const char *chunk_type2str(chunk_type2str_t type)
{
        static char *chunk_type2str[] = {CHUNK_TYPE};

        if (type > __RAW_CHUNK__) {
                int __i = 0, __j __attribute((unused));
                __j = 1 / __i;
        }

        return chunk_type2str[type];
}

static inline int chunk_str2type(const char *name)
{
        int i;
        static char *chunk_type2str[] = {CHUNK_TYPE};

        for (i = __NULL_CHUNK__; i <= __RAW_CHUNK__; i++) {
                if (strcmp(name, chunk_type2str[i]) == 0)
                        return i;
        }

        int __i = 0, __j __attribute((unused));
        __j = 1 / __i;

        return -1;
}

inline static int chkid_continuous(const chkid_t *chkid, int chknum)
{
        int i;

        for (i = 0; i < chknum; i++) {
                if (chkid[i].idx != chkid->idx + i)
                    return 0;
        }

        return 1;
}

inline static int chkid_ordered(const chkid_t *chkid, int chknum)
{
        int i;

        /**
         * chknum >= 1 normally.
         * if chknum = 1, then skip the for loop.
         * else chkid[prev].idx must less than chkid[current].idx
         */
        for (i = 1; i < chknum; i++) {
                if (chkid[i - 1].idx >= chkid[i].idx)
                    return 0;
        }

        return 1;
}

inline static int chkid_ispool(const chkid_t *chkid)
{
        if (chkid->type == __POOL_CHUNK__
            || chkid->type == __POOL_SUB_CHUNK__)
                return 1;
        else
                return 0;
}

inline static int chkid_isvolmeta(const chkid_t *chkid)
{
        if (chkid->type == __VOLUME_CHUNK__
            || chkid->type == __VOLUME_SUB_CHUNK__)
                return 1;
        else
                return 0;
}

inline static int chkid_isvol(const chkid_t *chkid)
{
        if (chkid->type == __VOLUME_CHUNK__
            || chkid->type == __VOLUME_SUB_CHUNK__
            || chkid->type == __RAW_CHUNK__)
                return 1;
        else
                return 0;
}

inline static int chkid_isnull(const chkid_t *chkid)
{
        if (chkid->id == CHKID_NULL) {
                //YASSERT(chkid->type == __NULL_CHUNK__);
                return 1;
        } else
                return 0;
}

inline static int chkid_isroot(const chkid_t *chkid)
{
        if (chkid->id == ROOTID) {
                //YASSERT(chkid->type == __NULL_CHUNK__);
                return 1;
        } else
                return 0;
}

inline static void rootid_init(chkid_t *chkid)
{
        memset(chkid, 0x0, sizeof(*chkid));
        chkid->id = ROOTID;
        chkid->type = __POOL_CHUNK__;
        chkid->idx = 0;
}

inline static int chkid_isvalid(const chkid_t *chkid)
{
        if (chkid_ispool(chkid) || chkid_isvol(chkid))
            return 1;
        else
            return 0;
}

/**
 * ID for net
 */

#pragma pack(8)

//typedef verid64_t nid_t;
typedef struct {
        int16_t id;
} nid_t;

typedef struct {
        nid_t id;
        uint16_t status;
} reploc_t;

#pragma pack()


typedef nid_t diskid_t;
nid_t g_local_nid;

static inline void diskid2loc(reploc_t *reploc, const nid_t *diskid, int count)
{
        int i;

        for (i = 0; i < count; i++) {
                reploc[i].id = diskid[i];
                reploc[i].status = 0;
        }
}

#define NID_FORMAT      "%hd"
#define NID_ARG(_id)    (_id)->id

static inline void nid2str(char *str, const nid_t *nid)
{
        snprintf(str, MAX_NAME_LEN, NID_FORMAT, NID_ARG(nid));
}

static inline void str2nid(nid_t *nid, const char *str)
{
        //sscanf(str, "%lu.%u.%[^[][%lu]", &chkid->id, &chkid->version, type, &chkid->idx);
        sscanf(str, "%hu", &nid->id);
        //nid->status = 0;
}

/**
 * ID format macro
 */
#define CHKID_FORMAT   "%s.%ju.%u"
#define CHKID_ARG(__id__)  chunk_type2str((__id__)->type), (__id__)->id, (__id__)->idx

#define DISKID_FORMAT NID_FORMAT
#define DISKID_ARG NID_ARG

#define LOC_FORMAT   "%u.%u"
#define LOC_ARG(__loc__)  (__loc__)->diskid, (__loc__)->idx
/**
 * ID compare
 */

static inline int __id_cmp__(uint64_t id1, uint64_t id2)
{
        if (id1 == id2) {
                return 0;
        } else if (id1 < id2) {
                return -1;
        } else {
                return 1;
        }
}

static inline int chkid_cmp(const chkid_t *id1, const chkid_t *id2)
{
        int ret;

        ret = __id_cmp__(id1->type, id2->type);
        if (unlikely(ret))
                return ret;

        ret = __id_cmp__(id1->id, id2->id);
        if (unlikely(ret))
                return ret;

        ret = __id_cmp__(id1->idx, id2->idx);
        if (unlikely(ret))
                return ret;

        return 0;
}

#define fileid_cmp chkid_cmp
#define oid_cmp chkid_cmp

typedef enum {
        NET_HANDLE_NULL,
        NET_HANDLE_PERSISTENT,  /* Constant connect */
        NET_HANDLE_TRANSIENT,   /* Temporary connect */
} net_handle_type_t;

typedef struct {
        uint32_t addr;
        uint32_t seq;
        int sd;
        int type;
        int outst_req;
        uintptr_t rdma_handler;
} sockid_t;

typedef struct {
        net_handle_type_t type;
        union {
                nid_t nid;     ///< when type == NET_HANDLE_PERSISTENT
                sockid_t sd;   ///< when type == NET_HANDLE_TRANSIENT
        } u;
} net_handle_t;

static inline int sockid_cmp(const sockid_t *sock1, const sockid_t *sock2)
{
        if (sock1->addr < sock2->addr)
                return -1;
        else if (sock1->addr > sock2->addr)
                return 1;

        if (sock1->seq < sock2->seq)
                return -1;
        else if (sock1->seq > sock2->seq)
                return 1;

        if (sock1->sd < sock2->sd)
                return -1;
        else if (sock1->sd > sock2->sd)
                return 1;

        return 0;
}

static inline int nid_cmp(const nid_t *lhs, const nid_t *rhs)
{
        if (lhs->id < rhs->id)
                return -1;
        else if (lhs->id > rhs->id)
                return 1;
        return 0;
}

static inline int net_handle_cmp(const net_handle_t *lhs, const  net_handle_t *rhs)
{
        if (lhs->type == rhs->type) {
                if (lhs->type == NET_HANDLE_PERSISTENT) {
                        return nid_cmp(&lhs->u.nid, &rhs->u.nid);
                } else {
                        return sockid_cmp(&lhs->u.sd, &rhs->u.sd);
                }
        } else if (lhs->type < rhs->type)
                return -1;
        else
                return 1;
}

#define nid_cmp nid_cmp

static inline void id2nh(net_handle_t *nh, const nid_t *id)
{
        nh->u.nid = *id;
        nh->type = NET_HANDLE_PERSISTENT;
}

static inline void sock2nh(net_handle_t *nh, const sockid_t *id)
{
        nh->u.sd = *id;
        nh->type = NET_HANDLE_TRANSIENT;
}

static inline void net_handle_reset(void *_nh)
{
        net_handle_t *nh = (net_handle_t *)_nh;

        nh->type = NET_HANDLE_NULL;
}

static inline void fid2cid(chkid_t *chkid, const chkid_t *fileid, uint64_t idx)
{
        *chkid = *fileid;
        chkid->idx = idx;
        chkid->type = __RAW_CHUNK__;
}

static inline void tid2cid(chkid_t *chkid, const chkid_t *tid, uint32_t off) {
        chkid->type = __RAW_CHUNK__;
        chkid->id = tid->id;
        chkid->idx = tid->idx * FILE_PROTO_EXTERN_ITEM_COUNT + off;
}

static inline void cid2fid(chkid_t *fileid, const chkid_t *chkid)
{
        *fileid = *chkid;
        fileid->idx = 0;
        fileid->type = __VOLUME_CHUNK__;
}

/**
 * @param fileid L2 chkid
 * @param chkid raw chkid
 */
static inline void cid2tid(chkid_t *fileid, const chkid_t *chkid) {
        assert(chkid->type == __RAW_CHUNK__);
        *fileid = *chkid;

#if 1
        fileid->idx = chkid->idx / FILE_PROTO_EXTERN_ITEM_COUNT;
#else
        fileid->idx = (chkid->idx / FILE_PROTO_EXTERN_ITEM_COUNT) + chkid->idx % 10;
#endif
        fileid->type = __VOLUME_SUB_CHUNK__;
}

/**
 * @param topid L1 chkid
 * @param chkid raw chkid
 */
static inline void cid2topid(chkid_t *top_chkid, const chkid_t *chkid) {
        assert(chkid->type == __RAW_CHUNK__);

        top_chkid->type = __VOLUME_CHUNK__;
        top_chkid->id = chkid->id;

        if (chkid->idx < FILE_PROTO_ITEM_COUNT * FILE_PROTO_EXTERN_ITEM_COUNT) {
                top_chkid->idx = 0;
        } else {
                top_chkid->idx = 1 + (chkid->idx - FILE_PROTO_ITEM_COUNT * FILE_PROTO_EXTERN_ITEM_COUNT) /
                                     (FILE_PROTO_EXTERN_ITEM_COUNT * FILE_PROTO_EXTERN_ITEM_COUNT);
        }
}

/**
 *
 * @param topid L1 chkid
 * @param chkid L2 chkid
 */
static inline void tid2topid(chkid_t *top_chkid, const chkid_t *tid) {
        assert(tid->type == __VOLUME_SUB_CHUNK__);

        top_chkid->type = __VOLUME_CHUNK__;
        top_chkid->id = tid->id;

        if (tid->idx < FILE_PROTO_ITEM_COUNT) {
                top_chkid->idx = 0;
        } else {
                top_chkid->idx = 1 + (tid->idx - FILE_PROTO_ITEM_COUNT) / FILE_PROTO_EXTERN_ITEM_COUNT;
        }
}

/**
 * 每个chunk的parent有两种定义，一指所在控制器；二指在meta chunk树里的父节点所对应的控制器
 *
 * 对subpool, subvol, raw，两者是一致的
 * 对pool，vol，两者不同；sqlite里记录的是第二种含义
 *
 * 不同的API，所指称的parent有所不同，要注意区别对待，具体情况具体分析
 *
 * @param parent
 * @param chkid
 */
static inline void cid2parent(chkid_t *parent, const chkid_t *chkid) {
        if (chkid->type == __RAW_CHUNK__) {
                cid2fid(parent, chkid);
        } else if (chkid->type == __VOLUME_SUB_CHUNK__) {
                cid2fid(parent, chkid);
                parent->type = __VOLUME_CHUNK__;
        } else if (chkid->type == __POOL_SUB_CHUNK__) {
                cid2fid(parent, chkid);
                parent->type = __POOL_CHUNK__;
        } else {
                assert(chkid->type == __VOLUME_CHUNK__ || chkid->type == __POOL_CHUNK__);
                *parent = *chkid;
                parent->idx = 0;
        }
}

static inline void chkid2ctl(const chkid_t *chkid, chkid_t *groupid)
{
        switch (chkid->type) {
                case __POOL_CHUNK__:
                case __POOL_SUB_CHUNK__:
                        *groupid = *chkid;
                        groupid->idx = 0;
                        groupid->type = __POOL_CHUNK__;
                        break;
                case __VOLUME_CHUNK__:
                case __VOLUME_SUB_CHUNK__:
                case __RAW_CHUNK__:
                        *groupid = *chkid;
                        groupid->idx = 0;
                        groupid->type = __VOLUME_CHUNK__;
                        break;
                default:
                        assert(0 && "err!");
                        break;
        }
}

static inline int is_pool(const chkid_t *chkid) {
        return chkid->type == __POOL_CHUNK__;
}

static inline int is_volume(const chkid_t *chkid) {
        return chkid->type == __VOLUME_CHUNK__ && chkid->idx == 0;
}

static inline int is_top_object(const chkid_t *chkid) {
        return is_pool(chkid) || is_volume(chkid);
}

inline static int chkid_sametid(const chkid_t *chkid, int chknum, const chkid_t *tid)
{
        int i;
        chkid_t tmp;

        for (i = 0; i < chknum; i++) {
                cid2tid(&tmp, &chkid[i]);

                if (chkid_cmp(&tmp, tid))
                    break;
        }

        return i;
}

static inline const char *id2str(const chkid_t *chkid)
{
        //线程静态变量　并发会不会有问题 ?
        //小心使用！
        static __thread char buf[MAX_NAME_LEN];

        if (chkid->id == 0) {
                buf[0] = '\0';
        } else {
                sprintf(buf, CHKID_FORMAT, CHKID_ARG(chkid));
        }

        return buf;
}

#if 0
static inline const chkid_t *str2id(const char *str)
{
        int ret;
        static __thread chkid_t chkid;

        if (str[0] == '\0') {
                memset(&chkid, 0x0, sizeof(chkid));
                return &chkid;
        }

        if (memcmp(str, "pool", 4) == 0) {
                ret = sscanf(str, "pool.%lu.%u", &chkid.id, &chkid.idx);
                if (ret != 2)
                        return NULL;

                chkid.type = __POOL_CHUNK__;
                chkid.idx = 0;
        } else if (memcmp(str, "vol", 3) == 0) {
                ret = sscanf(str, "vol.%lu.%u", &chkid.id, &chkid.idx);
                if (ret != 2)
                        return NULL;

                chkid.type = __VOLUME_CHUNK__;
        } else if (memcmp(str, "subvol", 6) == 0) {
                ret = sscanf(str, "subvol.%lu.%u", &chkid.id, &chkid.idx);
                if (ret != 2)
                        return NULL;

                chkid.type = __VOLUME_SUB_CHUNK__;
        } else if (memcmp(str, "subpool", 7) == 0) {
                ret = sscanf(str, "subpool.%lu.%u", &chkid.id, &chkid.idx);
                if (ret != 2)
                        return NULL;

                chkid.type = __POOL_SUB_CHUNK__;
        } else {
                ret = sscanf(str, "raw.%lu.%u", &chkid.id, &chkid.idx);
                if (ret != 2)
                        return NULL;

                chkid.type = __RAW_CHUNK__;
        }

        return &chkid;
}
#endif

static inline void str2chkid(chkid_t *chkid, const char *str)
{
        int ret;

        if (str[0] == '\0') {
                memset(chkid, 0x0, sizeof(*chkid));
                return;
        }

        if (memcmp(str, "pool", 4) == 0) {
                ret = sscanf(str, "pool.%lu.%u", &chkid->id, &chkid->idx);
                if (ret != 2)
                        return ;

                chkid->type = __POOL_CHUNK__;
                chkid->idx = 0;
        } else if (memcmp(str, "vol", 3) == 0) {
                ret = sscanf(str, "vol.%lu.%u", &chkid->id, &chkid->idx);
                if (ret != 2)
                        return ;

                chkid->type = __VOLUME_CHUNK__;
        } else if (memcmp(str, "subvol", 6) == 0) {
                ret = sscanf(str, "subvol.%lu.%u", &chkid->id, &chkid->idx);
                if (ret != 2)
                        return ;

                chkid->type = __VOLUME_SUB_CHUNK__;
        } else if (memcmp(str, "subpool", 7) == 0) {
                ret = sscanf(str, "subpool.%lu.%u", &chkid->id, &chkid->idx);
                if (ret != 2)
                        return ;

                chkid->type = __POOL_SUB_CHUNK__;
        } else {
                ret = sscanf(str, "raw.%lu.%u", &chkid->id, &chkid->idx);
                if (ret != 2)
                        return ;

                chkid->type = __RAW_CHUNK__;
        }

        return ;
}

static inline void mk_volid(volid_t *volid, uint64_t id) {
        volid->type = __VOLUME_CHUNK__;
        volid->id = id;
        volid->idx = 0;
}

#endif
